#!/usr/bin/env python
from __future__ import generators

from os3.dbm.dbmanager import DBManager
import utils
from errors import *
from os3.types.date import Date
Date.default_format = '%Y-%m-%d %H:%M:%S'

class UserManager ( object ):
	# ******************************************************
	# __init__() - costruttore
	#              <dbm> e' un'istanza di DBManager
	#              <table_name> e' il nome della tabella
	#              <field_map> e' un dizionario di mappatura attributo_utente -> nome_campo
	# ******************************************************
	def __init__ ( self, dbm, table_name, field_map = None, pwd_encr_type = 'md5' ):
		super ( UserManager, self ).__init__()
		self._dbm = dbm
		self._table_name = table_name
		self._pwd_encr_type = pwd_encr_type

		if field_map:
			self._field_map = field_map
		else:
			self._field_map = {
				'username': 'login',
				'password': 'passwd',
				'uid': 'id',
				'gid': 'gid',
				'last_log': 'last_log',
				'creation_date': 'created',
				'enabled': 'enabled',
			}

		if not self._field_map.has_key ( 'username' ):
			self._field_map["username"] = 'username'


	# ******************************************************
	# get() - recupera i dati di un utente
	#         <user> puo' essere un intero (uid) o una stringa (username)
	# ******************************************************
	def get ( self, user ):
		if isinstance ( user, str ):
			sql_filter = self._field_map["username"] + " = '%s'" % user
		else:
			sql_filter = self._field_map["uid"] + " = %d" % user

		r = self._dbm.single_row ( self._table_name, self._field_map.values(), sql_filter )
		if not r: return None

		return self._make_user_dict ( r )


	# ******************************************************
	# users() - restituisce la lista degli utenti
	# ******************************************************
	def users ( self ):
		retval = []
		rs = self._dbm.select ( self._table_name, self._field_map.values() )
		if not rs: rs = []
		for r in rs:
			retval.append ( self._make_user_dict ( r ) )
		if rs: rs.close()
		return retval
		

	# ******************************************************
	# add() - aggiunge un utente
	#         <user> e' un dizionario di attributi
	# ******************************************************
	def add ( self, user ):
		uid = user.get ( "uid" )
		if uid is not None:
			user = get ( uid )
			if user: raise UserAlreadyExistsError
				
		username = user [ "username" ]
		username = username.strip()
		if not username: raise InvalidUserNameError
		
		u = self.get ( username )
		if u: raise UserAlreadyExistsError

		user["username"] = username
		user["creation_date"] = Date()
		user["password"] = utils.encrypt_pwd ( user.get ( "password", "" ), self._pwd_encr_type )
		
		user = self._make_user_dbdict ( user )

		self._dbm.insert ( self._table_name, user )


	# ******************************************************
	# delete() - cancella un utente
	#            <user> puo' essere un intero (uid) o una stringa (username)
	# ******************************************************
	def delete ( self, user ):
		user = self.get ( user )
		if not user: raise UserNotFoundError

		self._dbm.delete ( self._table_name, self._field_map["username"] + " = '%s'" % user["username"] )


	# ******************************************************
	# modify() - modifica uno o piu' attributi di un utente
	#            <user> puo' essere un intero (uid) o una stringa (username)
	#            <attrs> e' il dizionario di attributi
	# ******************************************************
	def modify ( self, user, attrs ):
		user = self.get ( user )
		if not user: raise UserNotFoundError

		for k in attrs:
			user[k] = attrs[k]

		dbdict = self._make_user_dbdict ( user )

		self._dbm.update ( self._table_name, dbdict, self._field_map["username"] + " = '%s'" % user["username"] )

		
	# ******************************************************
	# change_pwd() - modifica la password di un utente
	#            <user> puo' essere un intero (uid) o una stringa (username)
	#            <passwd> e' la nuova password
	# ******************************************************
	def change_pwd ( self, user, passwd ):
		udict = self.get ( user )
		if not udict: raise UserNotFoundError

		udict["password"] = utils.encrypt_pwd ( passwd, self._pwd_encr_type )

		self.modify ( user, udict )


	####################################################
	# INTERNAL METHODS
	####################################################

	def _iter_user_file ( self ):
		for l in file ( self._filename ):
			l = l.strip()
			if not l or l[0] == '#': continue
			yield l.split ( ':' )


	def _make_user_dict ( self, dbret ):

		rev_dict = {}
		for k, v in self._field_map.iteritems():
			rev_dict[v] = k

		retval = {
			"username": "",
			"password": "",
			"uid": 0,
			"gid": 0,
			"last_log": None,
			"creation_date": None,
			"enabled": 1,
			"exp_date": None
		}
		
		for k, v in dbret.iteritems():
			retval[rev_dict[k]] = v

		#remap date values
		if retval["last_log"]: retval["last_log"] = Date(str(retval["last_log"]))
		if retval["creation_date"]: retval["creation_date"] = Date(str(retval["creation_date"]))
		if retval["exp_date"]: retval["exp_date"] = Date(str(retval["exp_date"]))

		return retval


	def _make_user_dbdict ( self, user ):
		retval = {}
		for attr, field in self._field_map.iteritems():
			retval[field] = user.get ( attr, "" )
			
		#remap date values
		if retval.get ( "last_log" ): retval["last_log"] = str ( retval["last_log"] )
		if retval.get ( "creation_date" ): retval["creation_date"] = str ( retval["creation_date"] )
		if retval.get ( "exp_date" ): retval["exp_date"] = str ( retval["exp_date"] )

		return retval

